/*
** Copyright (C) 2009 Joshua Brent Marsh <joshua@icub3d.com>
**  
** This file is part of icub3d-adt
**
** icub3d-adt is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 3 of the License, or
** (at your option) any later version.
**  
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**  
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
**  
*/

/* $Id: string.c 45 2009-11-17 02:12:27Z joshua@icub3d.com $ */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <unistd.h>
#include <malloc.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>

#include "istring.h"
#include "utilities.h"
#include "function_prototypes.h"

int
strmcat (char **dest, const char *src)
{
  /* We are using doing basically the same thing as strmncat. */
  return strmncat (dest, src, 0);
}

int
strmncat (char **dest, const char *src, const size_t size)
{
  size_t len = 0;

  /* We won't need to do anything if the src is NULL. */
  if (src == NULL)
    return 1;

  /* If size is 0, work under the assumption that we are making space. */
  if (size == 0)
    {
      len = strlen (src);

      /* *dest could be null, meaning we basically just malloc. */
      if (*dest == NULL)
	{
	  *dest = malloc (len + 1);
	  if (*dest == NULL)
	    return 0;
	  
	  (*dest)[0] = '\0';
	}
      /* We need to make memory for dest + src. */
      else
	{
	  *dest = realloc (*dest, strlen (*dest) + len + 1);
	  if (*dest == NULL)
	    return 0;
	}
    }
 
  /* Append src to *dest. */
  strncat (*dest, src, (size == 0 ? len : size));
  
  return 1;
}

List *
strltok (const char *line, const char *delims, const char *quotes, 
	 const char *nulls)
{
  List *l;
  char * cur;
  char *begin;
  char *end;
  char *temp;
  int estate;
  int tmp_errno;

  /* Create the list we are returning. */
  l = list_create ();
  if (l == NULL)
    return NULL;

  /* Remove const, even though we aren't going to modify it. */
  cur = (char *)line;

  /* We are going to loop through the line until we don't have any
     more segments. */
  while (*cur != '\0' && strchr (nulls, *cur) == NULL)
    {
      estate = get_segment (&cur, &begin, &end, delims, quotes, nulls);
      if (estate == ISS_FAIL)
	{
	  tmp_errno = errno;
	  list_destroy (l, &dest_string);
	  errno = tmp_errno;
	  return NULL;
	}

      /* Our current segment blank (""). */
      if (end - begin <= 0)
	{
	  temp = malloc (1);
	  if (temp == NULL)
	    {
	      tmp_errno = errno;
	      list_destroy (l, &dest_string);
	      errno = tmp_errno;
	      return NULL;
	    }
	  
	  temp[0] = '\0';
	}
      /* Make a copy of the current segment. */
      else
	{
	  temp = malloc ((end - begin) + 1);
	  if (temp == NULL)
	    {
	      tmp_errno = errno;
	      list_destroy (l, &dest_string);
	      errno = tmp_errno;
	      return NULL;
	    }
	  
	  strncpy (temp, begin, end - begin);
	  temp[end-begin] = '\0';
	}

      if (!list_insert_last (l, temp))
	{
	  tmp_errno = errno;
	  free (temp);
	  list_destroy (l, &dest_string);
	  errno = tmp_errno;
	  return NULL;
	}
    }

  /* We need to do it one more time for the last item. */
  if ((*cur == '\0' || strchr (nulls, *cur) != NULL) &&
      strchr (delims, *(cur - 1)) != NULL)
    {
      temp = malloc (1);
      if (temp == NULL)
	{
	  tmp_errno = errno;
	  list_destroy (l, &dest_string);
	  errno = tmp_errno;
	  return NULL;
	}

      temp[0] = '\0';

      if (!list_insert_last (l, temp))
	{
	  tmp_errno = errno;
	  free (temp);
	  list_destroy (l, &dest_string);
	  errno = tmp_errno;
	  return NULL;
	}
    }

  return l;
}

int
strdcat (char **dest, const char *delim, const size_t count, ...)
{
  va_list srcs;
  int ret;

  /* We do the same thing as the non-variadic version, so just use it. */
  va_start (srcs, count);
  ret = vstrdcat (dest, delim, count, srcs);
  va_end (srcs);

  return ret;
}

int
vstrdcat (char **dest, const char *delim, const size_t count, va_list srcs)
{
  va_list csrcs;
  int ret;

  /* We are doing the same a the numeric versions with size = 0. */
  va_copy (csrcs, srcs);
  ret = vstrdncat (dest, delim, 0, count, srcs);
  va_end (csrcs);

  return ret;
}

int
strdncat (char **dest, const char *delim, const size_t size, 
	  const size_t count, ...)
{
  va_list srcs;
  int ret;

  /* We are doing the same thing as the non-variadic version. */
  va_start (srcs, count);
  ret = vstrdncat (dest, delim, size, count, srcs);
  va_end (srcs);

  return ret;  
}

int
vstrdncat (char **dest, const char *delim, const size_t size, 
	   const size_t count, va_list srcs)
{
  va_list csrcs;
  const char *s;
  size_t total = 0;
  size_t x;
  size_t dlen = strlen (delim);
  int tmp_errno;

  /* If dest is NULL and size > 0, we'll need to malloc space to start. */
  if (*dest == NULL && size > 0)
    {
      *dest = malloc (size + 1);
      if (*dest == NULL)
	return 0;

      (*dest)[0] = '\0';
    }

  /* Loop through all the sources. */
  va_copy (csrcs, srcs);
  for (x = 0; x < count; x++)
    {
      /* They will always be a string. */
      s = va_arg (csrcs, const char *);

      /* We are just going to keep appending and mallocing. */
      if (size == 0)
	{
	  /* Append s to the end of the destination. */
	  if (strmncat (dest, s, 0) == 0)
	    {
	      tmp_errno = errno;
	      va_end (csrcs);
	      errno = tmp_errno;
	      return 0;
	    }

	  /* We don't want to append the delimiter on the last run. */
	  if (x != count - 1)
	    {
	      /* Append the delimiter. */
	      if (strmncat (dest, delim, 0) == 0)
		{
		  tmp_errno = errno;
		  va_end (csrcs);
		  errno = tmp_errno;
		  return 0;
		}
	    }
	}
      /* We are only going to put at most size characters. */
      else
	{
	  /* Append as much as we can to the end of the destination. */
	  if (strmncat (dest, s, size - total) == 0)
	    {
	      tmp_errno = errno;
	      va_end (csrcs);
	      errno = tmp_errno;
	      return 0;
	    }
	  
	  /* Check to see if we have more room to append something. */
	  total += strlen (s);
	  if (total >= size)
	    break;
	  
	  /* We don't want to append the delimiter on the last run. */
	  if (x != count - 1)
	    {
	      /* Append as much of the delimiter as we can. */
	      if (strmncat (dest, delim, size - total) == 0)
		{
		  tmp_errno = errno;
		  va_end (csrcs);
		  errno = tmp_errno;
		  return 0;
		}
	      
	      /* Check to see if we have more room to append something. */
	      total += dlen;
	      if (total >= size)
		break;
	    }
	}
    }

  va_end (csrcs);

  return 1;
}
